/*******************************************************************************
 * Copyright (c) 2002 Boris Pruessmann and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.core.internal.events;

import com.google.common.collect.Sets;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceSynchronizer;
import net.sourceforge.perforce.core.internal.utils.MetaFileWriter;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.team.core.TeamException;

import java.util.Set;

/**
 * @version $Revision: 488 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class MetaFileChangeListener implements IResourceChangeListener {
  // ---------------------------------------------------------------------------
  // Private Constants

  /**
   * consider the following changes types and ignore the others (e.g. marker and
   * description changes are ignored)
   */
  private int INTERESTING_CHANGES =
      IResourceDelta.CONTENT | IResourceDelta.MOVED_FROM | IResourceDelta.MOVED_TO
          | IResourceDelta.OPEN | IResourceDelta.REPLACED | IResourceDelta.TYPE;

  //----------------------------------------------------------------------------
  // -- Public Methods

  /** @see IResourceChangeListener#resourceChanged(IResourceChangeEvent) */
  public void resourceChanged(IResourceChangeEvent event) {
    try {
      final Set<IResource> changedContainers = Sets.newHashSet();
      event.getDelta().accept(new IResourceDeltaVisitor() {
        public boolean visit(IResourceDelta delta) {
          IResource resource = delta.getResource();

          if (resource.getType() == IResource.ROOT) {
            // continue with the delta
            return true;
          }

          if (resource.getType() == IResource.PROJECT) {
            // If the project is not accessible, don't process it
            if (!resource.isAccessible()) {
              return false;
            }
          }

          String name = resource.getName();
          int kind = delta.getKind();

          // if the file has changed but not in a way that we care
          // then ignore the change (e.g. marker changes to files).
          if (kind == IResourceDelta.CHANGED && (delta.getFlags() & INTERESTING_CHANGES) == 0) {
            return true;
          }

          IResource[] toBeNotified = new IResource[0];
          if (name.equals(MetaFileWriter.IGNORE_FILE)) {
            toBeNotified = handleChangedIgnoreFile(resource, kind);
          }

          if (toBeNotified.length > 0 && isModifiedbyThirdParty(resource)) {
            for (int i = 0; i < toBeNotified.length; i++) {
              changedContainers.add(toBeNotified[i]);
            }
          }

          return true;
        }
      }, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);

      if (!changedContainers.isEmpty()) {
        PerforceSynchronizer.getInstance().metaFilesChanged(
            changedContainers.toArray(new IContainer[changedContainers.size()]));
      }
    } catch (TeamException e) {
      PerforceCorePlugin.log(e.getStatus());
    } catch (CoreException e) {
      PerforceCorePlugin.log(e.getStatus());
    }
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  private boolean isModifiedbyThirdParty(final IResource resource) {
    if (!resource.exists()) {
      return true;
    }

    long modStamp = resource.getModificationStamp();
    Long whenWeWrote;
    try {
      whenWeWrote = (Long) resource.getSessionProperty(MetaFileWriter.MODSTAMP_KEY);
    } catch (CoreException e) {
      PerforceCorePlugin.log(e.getStatus());
      whenWeWrote = null;
    }

    return (whenWeWrote == null || whenWeWrote.longValue() != modStamp);
  }

  private IContainer[] handleChangedIgnoreFile(IResource resource, int kind) {
    IContainer changedContainer = resource.getParent();
    if (changedContainer.exists()) {
      return new IContainer[] {changedContainer};
    } else {
      return new IContainer[0];
    }
  }
}
